<template>
  <a-select
    v-bind="getProps"
    v-model:value="modelValue"
    :options="options"
    style="width: 100%"
    :filterOption="frontendSearch ? filterOption : false"
    :show-search="showSearch"
    @change="handleChange"
    @search="handleSearch"
    :autoClearSearchValue="true"
  >
    <template v-if="optionMode == OptionMode.api && loading" #suffixIcon>
      <LoadingOutlined spin />
    </template>
    <template v-if="isShowPagination" #tagRender="{ value: val, closable, onClose }">
      <a-tag :closable="closable" style="margin-right: 3px" @close="onClose">
        {{ getCurrentLabel(val) }}
      </a-tag>
    </template>
    <template v-if="isShowPagination" #dropdownRender="{ menuNode: menu }">
      <v-nodes :vnodes="menu" />
      <a-divider style="margin: 4px 0" />
      <div @mousedown="(e) => e.preventDefault()">
        <a-pagination v-model:current="query.currentPage" simple :total="total" class="pagination" />
      </div>
    </template>
  </a-select>
</template>
<script setup lang="ts">
import { filterOption } from '@/utils/index';
import { selectProps } from 'ant-design-vue/es/select';
import { OptionMode, SelectMode, RequestMethod } from '@/enums/commonEnum';
import type { PropType } from 'vue';
import { debounce, get, isArray, isFunction } from 'lodash';
import { isString } from 'lodash';
import { defHttp } from '@/utils/http';
import { LoadingOutlined } from '@ant-design/icons-vue';
const emits = defineEmits(['update:value', 'change']);
const props = defineProps({
  ...selectProps(),
  value: {
    type: [String, Array],
    default: () => null,
  },
  // 是否多选 true为多选，false为单选
  multiple: {
    type: Boolean,
    default: false,
  },
  // options数据来源，可选值：api, options, optionsFunc
  optionMode: {
    type: String,
    default: OptionMode.options,
  },
  // 数据查询接口，返回promise对象
  api: {
    type: Function as PropType<(arg?: Recordable) => Promise<any>>,
  },
  // 接口查询返回后，在该属性下获取，如返回结果是数组则直接取数组为下拉数据值， 若为对象，则去该字段下的值，如res.[resultField]
  resultField: {
    type: String,
  },
  // 接收一个函数，返回options数据
  optionsFunc: {
    type: Function as PropType<(arg?: Recordable) => Array<any>>,
  },
  // 是否开启后端查询
  isBackendSearch: {
    type: Boolean,
    default: false,
  },
  // 当开启后端查询时，定义搜素值对应的字段名
  queryField: {
    type: String,
    default: 'q',
  },
  //当开启后端查询时，定义初始值对应的查询字段名，用于分页选中数据回显值对应传参的key定义
  valueField: {
    type: String,
    default: 'v',
  },
  //是否分页 开启分页时默认开启后端查询，只有多选支持分页，单选不支持
  isPagination: {
    type: Boolean,
    default: false,
  },
  // 额外参数, optionMode为api的时候可用
  params: {
    type: Object,
    default: () => {},
  },
  method: {
    type: String,
    default: 'post',
  },
  isJoinVal: {
    type: Boolean,
    default: true,
  },
});
const VNodes = (_: any, { attrs }: any) => {
  return attrs.vnodes;
};
// 下拉数据
const options = ref<any[]>([]);
const initOptions = ref<any[]>([]);
const loading = ref(false);
const isMultiple = computed(() => props.multiple || props.mode === SelectMode.multiple);
// 当为api模式，且多选，isPagination为true时，显示分页
const isShowPagination = computed(() => props.optionMode == OptionMode.api && props.isPagination && isMultiple.value);
const frontendSearch = computed(() => props.showSearch && !props.isBackendSearch && !props.isPagination);
const backendSearch = computed(() => props.showSearch && (props.isBackendSearch || props.isPagination));
const getFieldNames = computed(() => {
  if (props.fieldNames) {
    return props.fieldNames;
  }
  return { value: 'value', label: 'label' };
});
const getProps = computed(() => {
  if (isMultiple.value) {
    return Object.assign({}, props, { mode: SelectMode.multiple });
  }
  return props as Recordable;
});
const modelValue = ref(props.value);
const query = reactive({
  currentPage: 1,
  pageSize: 10,
  searchText: '',
});
const total = ref(0);

onMounted(async () => {
  if (isShowPagination.value) {
    await fetchInitOptions();
  }
});

watch(
  props,
  (newValue) => {
    if (isMultiple.value) {
      if (newValue.value && isString(newValue.value)) {
        modelValue.value = newValue.value?.split(',');
      } else {
        modelValue.value = newValue.value ? newValue.value : [];
      }
    } else {
      modelValue.value = newValue.value;
    }
  },
  { immediate: true },
);
watchEffect(async () => {
  if (props.optionMode === OptionMode.api) {
    await fetch();
  } else if (props.optionMode === OptionMode.optionsFunc) {
    const optionsFunc = props.optionsFunc;
    if (optionsFunc && isFunction(optionsFunc)) {
      options.value = optionsFunc();
    }
  } else {
    options.value = props.options as any;
  }
});

function getCurrentLabel(key: string) {
  const initOption = initOptions.value.find((item: any) => item[getFieldNames.value.value!] == key);
  if (initOption) return initOption[getFieldNames.value.label!];
  const searchMatchOption = options.value.find((item: any) => item[getFieldNames.value.value!] == key);
  if (searchMatchOption) return searchMatchOption[getFieldNames.value.label!];
  return '未知';
}

function getFetchApi(api: any) {
  return !isFunction(api)
    ? (data: any) =>
        defHttp.request(
          { url: api, method: props.method, data },
          { joinParamsToUrl: props.method == RequestMethod.get },
        )
    : api;
}
async function fetchInitOptions() {
  const api = props.api;
  if (!api) return;
  const fetchApi = getFetchApi(api);
  initOptions.value = [];

  try {
    const res = await fetchApi({
      currentPage: 1,
      pageSize: 100,
      [props.valueField]: props.value,
      ...props.params,
    });
    if (isArray(res)) {
      initOptions.value = res;
      return;
    }
    if (props.resultField) {
      initOptions.value = get(res, props.resultField) || [];
    }
  } catch (error) {
    console.warn(error);
  }
}
const handleSearch = debounce((value) => {
  if (!backendSearch.value) return;
  query.currentPage = 1;
  query.searchText = value;
  fetch();
}, 1000);

async function fetch() {
  const api = props.api;
  if (!api) return;
  const fetchApi = getFetchApi(api);
  options.value = [];
  const param: any = {};
  if (isShowPagination.value) {
    param.currentPage = query.currentPage;
    param.pageSize = query.pageSize;
  }
  if (backendSearch.value) {
    param[props.queryField] = query.searchText;
  }
  try {
    loading.value = true;
    const res = await fetchApi({
      ...param,
      ...props.params,
    });
    if (isArray(res)) {
      options.value = res;
      return;
    }
    if (props.resultField) {
      options.value = get(res, props.resultField) || [];
      if (isShowPagination.value) {
        query.currentPage = res.currentPage;
        query.pageSize = res.pageSize;
        total.value = res.totalCount;
      }
    }
  } catch (error) {
    console.warn(error);
  } finally {
    loading.value = false;
  }
}

const handleChange = (value: any, array: any) => {
  modelValue.value = value;
  query.searchText = '';
  const emitVal = props.isJoinVal && isArray(value) ? value.join(',') : value;
  // 触发change事件
  emits('change', emitVal, array);
  // 更新数据
  emits('update:value', emitVal);
};
</script>
<style scoped lang="less">
.pagination {
  margin-left: 12px;
}
</style>
